package org.zdp.demo.config.okhttp;

import cn.hutool.json.JSONUtil;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.zdp.demo.config.okhttp.interceptor.NetworkInterceptor;
import org.zdp.demo.config.okhttp.interceptor.RetryInterceptor;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

@Slf4j
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(OkHttpProperties.class)
@ConditionalOnProperty(prefix = OkHttpProperties.PREFIX, value = "enable", havingValue = "true")
public class OkHttpConfiguration {

    private final OkHttpProperties properties;

    @Primary
    @Bean
    public OkHttpClient okHttpClient() {
        OkHttpProperties.Http http = properties.getHttp();
        OkHttpProperties.Proxy proxy = properties.getProxy();

        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                .retryOnConnectionFailure(false)
                .connectionPool(new ConnectionPool(http.getMaxIdleConnections(), http.getKeepAliveDuration(), TimeUnit.SECONDS))
                .connectTimeout(http.getConnectTimeout(), TimeUnit.SECONDS)
                .readTimeout(http.getReadTimeout(), TimeUnit.SECONDS)
                .writeTimeout(http.getWriteTimeout(), TimeUnit.SECONDS)
                .hostnameVerifier((hostname, session) -> true)
                //.addInterceptor(new RetryInterceptor(properties.getHttp().getMaxRetryCount()))
                ;

        // 启动代理
        if (proxy.isEnable()) {
            builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy.getHost(), proxy.getPort())));
            log.info("okhttp3代理启动,代理地址:[{}],端口:[{}]", proxy.getHost(), proxy.getPort());
        }

        // 添加日志
        if (properties.isLogger()) {
            // builder.addInterceptor(new NetworkInterceptor());
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(@NonNull String message) {
                    log.info(message);
                }
            });
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.interceptors().add(interceptor);
        }

        OkHttpClient okHttpClient = builder.build();
        log.info("okhttp3启动成功，配置参数:[{}]", JSONUtil.toJsonStr(properties));
        return okHttpClient;
    }

    @ConditionalOnBean(OkHttpClient.class)
    @Bean
    public OkHttpRestClient okHttpRestClient(@Qualifier("okHttpClient") OkHttpClient okHttpClient) {
        return new OkHttpRestClient(okHttpClient);
    }

    @Bean
    public X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
    }

    @Bean
    public SSLSocketFactory sslSocketFactory() {
        try {
            // 信任任何链接
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

}
